# coding=utf-8
"""
author = jamon
"""

import struct
from asyncio import Transport
from typing import Optional

import ujson
from obespoir.config import NodeType

from obespoir.rpcserver.push_lib import push_message

from obespoir.base.common_define import RpcMsgType
from obespoir.base.ob_exception import ObException
from obespoir.share.encodeutil import AesEncoder
from obespoir.base.ob_protocol import ObProtocol
from obespoir.rpcserver.route import rpc_message_handle
from obespoir.base.global_object import GlobalObject


class RpcProtocol(ObProtocol):
    """消息协议，包含消息处理"""

    def __init__(self):
        super().__init__()

        # (int, int, int)  -> (message_length, command_id, version)
        self.hand_fmt: str = "iii"
        self.head_len: int = struct.calcsize(self.hand_fmt)
        self.identifier: int = 0

        self.encode_ins = AesEncoder(
            GlobalObject().rpc_password,
            encode_type=GlobalObject().rpc_encode_type)
        self.version: int = 0

        self._buffer: bytes = b""    # 数据缓冲buffer

        # 消息头, list,   [message_length, command_id, version]
        self._head: Optional[list] = None
        self.transport: Optional[Transport] = None

    async def message_handle(self, command_id, version, data):
        """
        实际处理消息
        :param command_id:
        :param version:
        :param data:
        :return:
        """
        # print("rpc protocol message_handle:", command_id, data)
        if not isinstance(data, dict):
            data = data.decode("utf-8") if isinstance(data, bytes) else data
            data = ujson.loads(data)
        # 消息最初来源于哪个proxy节点
        session_id: Optional[str] = data.get("session_id", None)
        if session_id is None:
            self.error("rpc receive lose session_id", command_id=command_id,
                       data=data)
            raise ObException(name="RpcSessionIdNotExist", data=data)
        to, _ = session_id.rsplit("_", 1)
        seq = data.get("seq", None)
        msg_type = data.get("msg_type")
        if seq is None:
            self.error("rpc receive lose future seq",
                       command_id=command_id, data=data)
            raise ObException(name="RpcSeqNotExist", data=data)
        result = await rpc_message_handle(command_id, session_id, data)
        self.debug("rpc message_handle end", msg_type=msg_type, result=result)
        if RpcMsgType.REQUEST == msg_type:
            await self.response_message(
                command_id, result, session_id, to, seq=seq)
        # elif RpcMsgType.NOTIFY == msg_type and not GlobalObject().is_proxy():
        #     await push_message(NodeType.PROXY, command_id, result,
        #                        session_id, to,
        #                        msg_type=msg_type)

    async def response_message(self, command_id, message, session_id,
                               to=None, seq=None):
        self.debug("start", session_id=session_id, seq=seq, message=message, to=to)
        data = self.pack(message, command_id, session_id, to, seq=seq,
                         msg_type=RpcMsgType.RESPONSE)
        self.transport.write(data)
        self.debug("end", session_id=session_id, seq=seq, message=message, to=to)

    def connection_made(self, transport):
        self.transport = transport
        address = transport.get_extra_info('peername')
        self.debug("accepted connection", address=address)

    def data_received(self, data):
        self.debug("rpc server received", data=data)
        super().data_received(data)

    def eof_received(self):
        self.debug("received EOF")
        if self.transport.can_write_eof():
            self.transport.write_eof()

    def connection_lost(self, error):
        if error:
            self.error("connection lost error", error=error)
        else:
            self.debug("closing connection")
        super().connection_lost(error)
